/*
 * $Id: auth.c,v 1.6 2005-08-05 06:48:31 bacon Exp $
 */

#include <xp/utl/auth.h>
#include <xp/bas/string.h>
#include <xp/bas/mstring.h>
#include <xp/bas/locale.h>
#include <xp/bas/memory.h>
#include <xp/bas/sysapi.h>
#include <security/pam_appl.h>

static int __conv_direct (
	int num_msg, const struct pam_message** msg, 
	struct pam_response** resp, void* appdata_ptr)
{
	struct pam_response* reply;
	int i;

	if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG) return PAM_CONV_ERR;

	reply = xp_calloc (num_msg, sizeof(struct pam_response));
	if (reply == XP_NULL) return PAM_BUF_ERR;

	for (i = 0; i < num_msg; i++) {
		reply[i].resp_retcode = 0;
		reply[i].resp = xp_mstrdup(appdata_ptr);
	}

	*resp = reply;
	return PAM_SUCCESS;
}

int xp_authuser (
	const xp_char_t* service, 
	const xp_char_t* user, const xp_char_t* pass)
{
	struct pam_conv conv;
	pam_handle_t* handle;
	int n;

#ifdef XP_CHAR_IS_MCHAR
	const xp_mchar_t* service_mb = service;
	const xp_mchar_t* user_mb = user;
	const xp_mchar_t* pass_mb = pass;
#else
	xp_mchar_t service_mb[XP_PATH_MAX + 1];
	xp_mchar_t user_mb[PAM_MAX_RESP_SIZE];
	xp_mchar_t pass_mb[PAM_MAX_RESP_SIZE];
	xp_size_t len;

	len = xp_wcstomcs(service, service_mb, xp_countof(service_mb));
	if (len == (xp_size_t)-1 || len < xp_strlen(service)) return -1;
	len = xp_wcstomcs(user, user_mb, xp_countof(user_mb));
	if (len == (xp_size_t)-1 || len < xp_strlen(user)) return -1;
	len = xp_wcstomcs (pass, pass_mb, xp_countof(pass_mb));
	if (len == (xp_size_t)-1 || len < xp_strlen(pass)) return -1;
#endif

	conv.conv = __conv_direct;
	conv.appdata_ptr = (void*)pass_mb;

	if ((n = pam_start(service_mb, user_mb, &conv, &handle)) != PAM_SUCCESS) {
		return -1;
	}

	if ((n = pam_authenticate(handle, PAM_SILENT)) != PAM_SUCCESS) {
		pam_end (handle, n);
		return -1;
	}

	if ((n = pam_acct_mgmt (handle, PAM_SILENT)) != PAM_SUCCESS) {
		pam_end (handle, n);
		return -1;
	}

	pam_end (handle, n);
	return 0;
}

int xp_chpasswd (
	const xp_char_t* service, 
	const xp_char_t* user, const xp_char_t* pass)
{
	struct pam_conv conv;
	pam_handle_t* handle;
	int n;

#ifdef XP_CHAR_IS_MCHAR
	const xp_mchar_t* service_mb = service;
	const xp_mchar_t* user_mb = user;
	const xp_mchar_t* pass_mb = pass;
#else
	xp_mchar_t service_mb[XP_PATH_MAX + 1];
	xp_mchar_t user_mb[PAM_MAX_RESP_SIZE];
	xp_mchar_t pass_mb[PAM_MAX_RESP_SIZE];
	xp_size_t len;

	len = xp_wcstomcs(service, service_mb, xp_countof(service_mb));
	if (len == (xp_size_t)-1 || len < xp_strlen(service)) return -1;
	len = xp_wcstomcs(user, user_mb, xp_countof(user_mb));
	if (len == (xp_size_t)-1 || len < xp_strlen(user)) return -1;
	len = xp_wcstomcs (pass, pass_mb, xp_countof(pass_mb));
	if (len == (xp_size_t)-1 || len < xp_strlen(pass)) return -1;
#endif

	conv.conv = __conv_direct;
	conv.appdata_ptr = (void*)pass_mb;

	if ((n = pam_start(service_mb, user_mb, &conv, &handle)) != PAM_SUCCESS) {
		return -1;
	}

	if ((n = pam_chauthtok(handle, PAM_SILENT)) != PAM_SUCCESS) {
		pam_end (handle, n);
		return -1;
	}

	pam_end (handle, n);
	return 0;
}
